package com.xiaoyu.lanling.feature.voicecall

import `in`.srain.cube.util.CLog
import `in`.srain.cube.util.LocalDisplay
import `in`.srain.cube.util.TimeUtils
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Intent
import android.os.SystemClock
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.AccelerateDecelerateInterpolator
import android.widget.TextView
import com.imuxuan.floatingview.FloatingMagnetView
import com.imuxuan.floatingview.FloatingView
import com.imuxuan.floatingview.MagnetViewListener
import com.xiaoyu.base.app.AppContext
import com.xiaoyu.base.app.GlobalUI
import com.xiaoyu.base.utils.RxUtils
import com.xiaoyu.base.utils.extensions.dp
import com.xiaoyu.base.utils.extensions.setOnClickDebounceListener
import com.xiaoyu.base.utils.time.CountDown
import com.xiaoyu.lanling.R
import com.yhao.floatwindow.*
import io.reactivex.Observable
import io.reactivex.disposables.Disposable
import java.util.concurrent.TimeUnit

class CallFloatWindowManager {

    private val viewHolderMap = mutableMapOf<String, MutableMap<String, ViewHolder>>()
    private val typeFloatView = "view"
    private val typeFloatWindow = "window"

    private var mIsMutualPublic: Boolean? = null
    private var mTimerDisposable: Disposable? = null

    private class ViewHolder(val floatView: View, val timeView: TextView, val statusView: TextView)

    @SuppressLint("InflateParams")
    private fun initFloatWindow(activity: Activity): IFloatWindow {
        val tag = getFloatWindowTag(activity)
        val floatWindow = FloatWindow.get(tag)
        if (floatWindow != null) {
            return floatWindow
        }
        val floatWindowView = inflateFloatView(activity, typeFloatWindow)
        val holder = getFloatViewHolder(activity, PermissionUtil.hasPermission(AppContext.getContext()))
        holder?.floatView?.setOnClickDebounceListener {
            val intent = Intent(AppContext.getContext(), activity.javaClass)
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP)
            AppContext.getContext().startActivity(intent)
        }
        FloatWindow.with(AppContext.getContext().applicationContext)
                .setTag(tag)
                .setView(floatWindowView)
                .setWidth(LocalDisplay.dp2px(64f))
                .setHeight(LocalDisplay.dp2px(72f))
                .setX(Screen.width, 0.8f)
                .setY(Screen.height, 0.3f)
                .setMoveType(MoveType.slide, 24, 24)
                .setMoveStyle(200, AccelerateDecelerateInterpolator())
                .setViewStateListener(sViewStateListener)
                .setPermissionListener(sPermissionListener)
                .setDesktopShow(true)
                .build()
        return FloatWindow.get(tag)
    }

    private fun inflateFloatView(activity: Activity, type: String): View {
        val layoutInflater = LayoutInflater.from(AppContext.getContext())
        val floatView = layoutInflater.inflate(R.layout.view_chat_call_float_window, null, false)
        floatView.let {
            val timeView: TextView = it.findViewById(R.id.chat_call_float_window_time)
            val statusView: TextView = it.findViewById(R.id.chat_call_float_window_status)
            statusView.text = "等待接通"

            val typeMap = mutableMapOf<String, ViewHolder>()
            typeMap[type] = ViewHolder(floatView, timeView, statusView)
            viewHolderMap.put(activity.javaClass.simpleName, typeMap)
        }
        return floatView
    }

    fun showCountdownFloatWindow(activity: Activity, remainMs: Long, needShow: Boolean, isMutualPublic: Boolean) {
        RxUtils.disposable(mTimerDisposable)
        val floatWindow = prepareFloatWindow(activity)

        if (needShow) {
            showFloatWindow(activity, floatWindow)
        }

        mIsMutualPublic = isMutualPublic
        val hasPermission = PermissionUtil.hasPermission(activity)
        val isShowStatus = remainMs == -1L
        processStatus(activity, hasPermission, isShowStatus)

        getFloatViewHolder(activity, hasPermission)?.timeView?.let {
            if (!isShowStatus) {
                val countdown = CountDown.fromNow(remainMs)
                createCountdownTimer(activity, countdown, hasPermission)
            }
        }
    }

    fun showFloatWindow(activity: Activity, startMs: Long, needShow: Boolean) {
        RxUtils.disposable(mTimerDisposable)
        val floatWindow = prepareFloatWindow(activity)

        if (needShow) {
            showFloatWindow(activity, floatWindow)

            val hasPermission = PermissionUtil.hasPermission(activity)

            val isShowStatus = startMs == -1L
            processStatus(activity, hasPermission, isShowStatus)
            getFloatViewHolder(activity, hasPermission)?.timeView?.let {
                if (!isShowStatus) {
                    createCountdownTimer(activity, hasPermission, startMs)
                }
            }
        }
    }

    fun getFloatWindow(activity: Activity): IFloatWindow? {
        val tag = getFloatWindowTag(activity)
        return FloatWindow.get(tag)
    }

    fun hideAndStopFloatWindow(activity: Activity) {
        RxUtils.disposable(mTimerDisposable)
        val tag = getFloatWindowTag(activity)
        val floatWindow: IFloatWindow? = FloatWindow.get(tag)
        floatWindow?.hide()
        FloatingView.get().remove()
    }

    fun destroyFloatWindow(activity: Activity) {
        val tag = getFloatWindowTag(activity)
        FloatWindow.destroy(tag)
        FloatingView.get().remove()
    }

    private fun getFloatViewHolder(activity: Activity, hasPermission: Boolean): ViewHolder? {
        return viewHolderMap[getFloatWindowTag(activity)]?.get(if (hasPermission) typeFloatWindow else typeFloatView)
    }

    fun createCountdownTimer(activity: Activity, countdown: CountDown, hasPermission: Boolean) {
        RxUtils.disposable(mTimerDisposable)
        mTimerDisposable = Observable.interval(0, 1000, TimeUnit.MILLISECONDS)
                .compose(RxUtils.ioToMainObservableScheduler())
                .subscribe {
                    if (countdown.isExpiredByServers) {
                        hideAndStopFloatWindow(activity)
                        return@subscribe
                    }
                    val timeView = getFloatViewHolder(activity, hasPermission)?.timeView
                    timeView?.text = countdown.remainSecondsByServers.toString()
                }
    }

    private fun createCountdownTimer(activity: Activity, hasPermission: Boolean, startMs: Long) {
        RxUtils.disposable(mTimerDisposable)
        mTimerDisposable = Observable.interval(0, 1000, TimeUnit.MILLISECONDS)
                .compose(RxUtils.ioToMainObservableScheduler())
                .subscribe {
                    val curMs = SystemClock.elapsedRealtime()
                    val durationS = (curMs - startMs) / 1000
                    val timeView = getFloatViewHolder(activity, hasPermission)?.timeView
                    if (durationS < TimeUtils.HOUR_MS) {
                        timeView?.text = TimeUtils.secondsToClockText(durationS)
                    } else {
                        timeView?.text = TimeUtils.secondsToDigitalText(durationS)
                    }
                }
    }

    private fun prepareFloatWindow(activity: Activity): IFloatWindow? {
        return getFloatWindow(activity) ?: return initFloatWindow(activity)
    }

    private fun showFloatWindow(activity: Activity, floatWindow: IFloatWindow?) {
        if (PermissionUtil.hasPermission(AppContext.getContext())) {
            FloatingView.get().remove()
            floatWindow?.show()
        } else {
            val floatView = inflateFloatView(activity, typeFloatView);

            val floatingMagnetView = FloatingMagnetView(AppContext.getContext())
            floatingMagnetView.layoutParams = ViewGroup.MarginLayoutParams(64.dp.toInt(), 72.dp.toInt())
            floatingMagnetView.x = LocalDisplay.SCREEN_WIDTH_PIXELS * 0.8F
            floatingMagnetView.y = LocalDisplay.SCREEN_HEIGHT_PIXELS * 0.3F
            floatingMagnetView.setMagnetViewListener(object : MagnetViewListener {
                override fun onClick(magnetView: FloatingMagnetView?) {
                    val intent = Intent(AppContext.getContext(), activity.javaClass)
                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP)
                    AppContext.getContext().startActivity(intent)
                }

                override fun onRemove(magnetView: FloatingMagnetView?) {
                }
            })
            floatingMagnetView.addView(floatView)

            FloatingView.get()
                    .customView(floatingMagnetView)
                    .show()
        }
    }

    private fun processStatus(activity: Activity, hasPermission: Boolean, isShowStatus: Boolean) {
        val holder = getFloatViewHolder(activity, hasPermission)
        holder?.statusView?.visibility = if (isShowStatus) View.VISIBLE else View.GONE
        holder?.timeView?.visibility = if (isShowStatus) View.GONE else View.VISIBLE
        if (mIsMutualPublic == true) {
            holder?.timeView?.visibility = View.INVISIBLE
        }
    }

    private fun getFloatWindowTag(activity: Activity): String {
        return activity.javaClass.simpleName
    }

    companion object {
        val TAG: String = CallFloatWindowManager::class.java.simpleName
        val instance by lazy { CallFloatWindowManager() }

        private val sPermissionListener: PermissionListener = object : PermissionListener {
            override fun onSuccess() {
                CLog.d(TAG, "onSuccess")
            }

            override fun onFail() {
                CLog.d(TAG, "onFail")
                GlobalUI.getInstance().showToast("悬浮窗权限获取失败，将无法使用语音悬浮窗")
            }
        }

        private val sViewStateListener: ViewStateListener = object : ViewStateListener {
            override fun onPositionUpdate(x: Int, y: Int) {
                CLog.d(TAG, "onPositionUpdate: x=$x y=$y")
            }

            override fun onShow() {
                CLog.d(TAG, "onShow")
            }

            override fun onHide() {
                CLog.d(TAG, "onHide")
            }

            override fun onDismiss() {
                CLog.d(TAG, "onDismiss")
            }

            override fun onMoveAnimStart() {
                CLog.d(TAG, "onMoveAnimStart")
            }

            override fun onMoveAnimEnd() {
                CLog.d(TAG, "onMoveAnimEnd")
            }

            override fun onBackToDesktop() {
                CLog.d(TAG, "onBackToDesktop")
            }
        }
    }
}